Smart Docs
Volume Number: 4
Issue Number: 2
Column Tag: C Workshop
Smart Documents 
By Joel McNamara, Watercourse Software, Bellevue, WA
Forked Tongue Programming: Data Forks, Resource Juggling, and Stand
Alone Documents
Most developers are quick to realize that a Macintosh file, whether application or
not, could easily be an advertisement for Double Mint Gum. Yes, it’s two, two, two
files in one. As “Inside Macintosh” is quick to point out, “Every file has a resource
fork and a data fork (either of which may be empty). The resource fork of an
application contains not only the resources used by the application but also the
application code itself. The data fork of an application can contain anything an
application wants to store there.” Big deal. Even most novices know that when an
application creates a document, it places the data into the data fork of a file. Pretty
basic stuff, even a bit dull and boring for most MacTutor readers.
But let’s step beyond the obvious for a moment, and concentrate on the quote “can
contain anything an application wants to store there.” If we can put anything in there,
how about data that only the application could access. No reading in other files, but
just operating on the information found within its data fork. Think of it as a stand
alone document. Perhaps a word processor that generates a file when double clicked
will run itself. Or a chart program that makes files you could modem to a business
associate without him needing the creator application. Something a little like “Glue,”
where you have the ability to view an application’s output without needing the
application. Runtime versions of data bases incorporate these features. Even
Microsoft is rumored to be considering a version of “Excel” that will create stand
alone spreadsheets.
Pretty neat stuff, but probably too tricky for the average programmer, right?
Wrong. The secret lies within effectively using the resource and data forks.
Stand Alone Document Theory
The reasons for developing such stand alone documents are quite obvious; the
ability to disseminate information without the need of a “mother” application to access
it; being able to address larger audiences with your output since not everyone needs the
creator; not needing sophisticated knowledge of an application to use the information
found in a document. All of these points and more make it worthwhile to consider
incorporating the ability to generate stand alone documents in your applications.
In thinking about such a project, there are several things to consider. The first
is the functionality you want the document to have. More than likely you’re not going
to want the stand alone file to do everything the original application does. Therefore,
you’ll need to include some secondary code that will simply display, scroll, print, or
do whatever.
But where does one put the code and resources actually needed to run the
document? If there is one set of CODE resources that run the main application and
another set that will run the stand alone document, won’t they conflict and cause
problems? Two theories can be used in developing stand alone document generators.
The first espouses putting all the required resources into a template file. The
generator then simply copies the template, gives the copied file a new name, and
inserts the desired information into the data fork. This is the technique used in the
stand alone text generator “Take A Letter.” The second method is cleaner, eliminating
the need for template files. The generator program actually contains the code needed to
run a stand alone document, although it may masquerade as another resource. The
generator creates a new file, copies in the resources, renames them, fills the data
fork, and sets the file’s attributes to be an application.
Stand Alone Document Practice
Enough theory, and on to the practice. What will be demonstrated is how to
construct a stand alone document generator that works with MacPaint format
documents. Three sources are included here. The first (PaintDisplay.c) is code for a
program that loads a Paint file stored in its data fork and then shows the upper left
hard corner of the bit mapped image. The second listing (PostCard.c) is for an
application called PostCard that builds a stand alone Paint document. (The code from
PaintDisplay will be inserted into this document generator.) And finally, PostCard.r
is the RMaker file. Although the sources are in Lightspeed C, the techniques should be
general enough to apply to a different variation of C or even another language.
(Lightspeed info: Both projects (PaintDisplay proj and PostCard proj) should include
MacTraps plus their own respective source files and be set to build applications.
PostCard’s creator type should be ‘PCRD’.)
The first step is to compile PaintDisplay.c. This source contains a standard
algorithm for displaying an image from a MacPaint format file. It initializes
everything, opens up its data fork, grabs bytes, puts up a window, displays the left
corner of the Paint document, and waits for a mouse click to quit. All in one breath.
Don’t try to run it quite yet though. If you do, it will be looking in its own data fork
for something that isn’t there yet, and your curiosity will be rewarded with a bomb.
The only real item in the code that needs discussing, is how one goes about
accessing the data fork. To quote from the source:
if (GetVol(&thisVolume,&vRef) != noErr)
ErrorRoutine(FALSE,0);
GetAppParms(&thisProgram,&temp,&thisHandle);
if (FSOpen(thisProgram,vRef,&srcFile) != noErr)
ErrorRoutine(TRUE,srcFile);
The first step is to determine what volume the application is on with a GetVol()
call. Next, use GetAppParms() to return the name of the current application. Now
pass the application’s name and the volume reference number to FSOpen(). It will
oblige by opening up your application’s data fork.
Resource Rascality
Once you’ve compiled PaintDisplay, the next task is to use RMaker on the
PostCard.r file. This file contains the resources for the menus, dialogs, bundles,
icons, etc. for the PostCard application. It also has the necessary resources to be used
in creating the stand alone document.
An often overlooked feature of RMaker is the ability to copy specific resources
from one file to another during the compile stage. By using the “.R” command you
could load, let’s say, a font from an existing file into the resource file you’re
currently compiling. The format is:
Type FONT = GNRL* the new resource type (FONT)
,8 * the new resource ID
.R * the RMaker directive
Fonts FONT 12 * the file “Fonts” and the resource
* (FONT 12) to copy into the new resource
RMaker is pretty lax as to letting you get away with things in certain areas. This
feature proves beneficial when building program generating applications. As
mentioned before, the biggest problem in writing an application that builds another, is
where to stick the code that will be spawned off. Obviously you can’t have a program
with two CODE 0 resources, at least without the potential for serious problems.
The solution is to build a new resource type of your choosing, and then through
RMaker, stuff the CODE into it. For example:
Type FAKE = GNRL * creates a resource type called FAKE...
,0 * with an ID of 0
.R * tells RMaker to load in something
SuperEdit CODE 0 * else, i.e., the jump table from
* a file called “SuperEdit”
This is what’s happening in PostCard.r. The previously compiled code from
PaintDisplay is being read in, and then assigned a new type and identification. The
CODE resource is still the same, but it’s gone into hiding by changing its name. It’s as
easy as that. When the resource file is compiled, you’ll have your CODE just sitting
there, ready to use, but disguised as something else that won’t conflict with the the
program that will be using it to create another application.
Taking a peek with ResEdit at the results. FAKE and DUMY are really code
resources, but with their new identities, don’t conflict with code resources found in
the application that will use them.
Two words of advice on using RMaker in such a fashion. The first is, RMaker
frowns on using a source file with a space in its name. Therefore, “My File” will
cause you problems but “MyFile” will not. The second caveat is to be aware of path
names. If all of your formats look all right, but RMaker still is complaining, odds are
you have an improperly laid out path. Double check it.
You’ll notice in the PostCard.r file, more than just CODE resources are being
changed. Remember, all compilers are not created equal. Some will generate just two
code segments (for under 32k of code, of course): 0 - the jump table, and 1 - the
actual code . Others, Lightspeed C for example, will generate more than two code
segments and a variety of other resource types where data is stored. That’s what all
the CRELs, DATAs, ZEROs, etc. are all about. When you are transferring and
converting code resources, be sure you get everything your particular compiler needs
for the application to run. At first put everything in, but after a while try some
experimenting. At times you’ll find you don’t need all of the data and support
resources.
Resource Moving Without ResEdit
The last step in the process is to compile PostCard.c. The compiled resources
from PostCard proj.Rsrc are copied in, and the application is ready to run.
From the Macintosh’s perspective, here’s what happens when the user selects
“Make a PostCard...” from the menu:
The SFPut() routine is called to select a name and location for the new file.
CreateResFile() is used to create the named file. Finder attributes are then set, and
the resource fork of the new file is opened.
Now the fun stuff. The code determines what the resource reference number of
the current application is (in this case PostCard). It then makes use of a programmer
defined function called ResTransfer().
ResTransfer(sourceRes, sourceID, destRes, destID)
ResType sourceRes, destRes;
int sourceID, destID;
Handle codeHandle;
codeHandle = NewHandle(0);
codeHandle = GetResource(sourceRes,sourceID);
UseResFile(resRef);
DetachResource(codeHandle);
AddResource(codeHandle, destRes, destID, “\p”);
if (ResError() != noErr) {
DoMessage(“\pSorry, couldn’t copy the required resource.”);
myError = 1;
}